/* 
 * Copyright [2018] [Alex/libo(liboware@gmail.com)]
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.network.service.nio;

import com.hyts.network.config.DefaultNetwork;
import com.hyts.network.config.SystemProperties;
import com.hyts.network.core.Server;
import com.hyts.network.error.ConnectionException;
import com.hyts.network.server.NewServerFactoryBean;
import com.hyts.network.task.AbstractExecutor;
import com.hyts.network.task.impl.HandlerTaskExecutor;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;

/**
 * @author LiBo/Alex
 * @see com.hyts.ext.network.server.NewServerFactoryBean
 * @since v1.0
 * @version v1.0
 */
public class NioServer extends NewServerFactoryBean<ExecutorService> implements Server {

    /**
     * field-name: logger
     * field-type: Logger
     * description: 日志输出器
     */
    private Logger logger = Logger.getLogger(this.getClass().getName());


    /**  
     * @constructor：NioSocketConnector  
     * @throws IOException  
     */ 
    public NioServer() throws IOException {
        super();
        this.setThreadPool(Executors.newFixedThreadPool(SystemProperties.OS_CORE_NUM*SystemProperties.DEFAULT_SINGLE_CORE_THREAD_NUM));
    }

    /**  
     * @constructor：NioSocketConnector  
     * @param isblock
     * @throws IOException  
     */ 
    public NioServer(boolean isblock) throws IOException {
        super(isblock);
        this.setThreadPool(Executors.newFixedThreadPool(SystemProperties.OS_CORE_NUM*SystemProperties.DEFAULT_SINGLE_CORE_THREAD_NUM));
    }

    /**  
     * @constructor：NioSocketConnector  
     * @param port
     * @param server  
     */ 
    public NioServer(int port, ServerSocketChannel server) {
        super(port, server);
        this.setThreadPool(Executors.newFixedThreadPool(SystemProperties.OS_CORE_NUM*SystemProperties.DEFAULT_SINGLE_CORE_THREAD_NUM));
    }

    /**  
     * @constructor：NioSocketConnector  
     * @param port
     * @throws IOException  
     */ 
    public NioServer(int port) throws IOException {
        super(port);
        this.setThreadPool(Executors.newFixedThreadPool(SystemProperties.OS_CORE_NUM*SystemProperties.DEFAULT_SINGLE_CORE_THREAD_NUM));
    }

    public NioServer(ServerSocketChannel server, int singleCpuThreadNum, int tryReconnectedTimes) {
        super(server, singleCpuThreadNum, tryReconnectedTimes);
        this.setThreadPool(Executors.newFixedThreadPool(SystemProperties.OS_CORE_NUM*SystemProperties.DEFAULT_SINGLE_CORE_THREAD_NUM));
    }

    public NioServer(ServerSocketChannel server, int singleCpuThreadNum) {
        super(server, singleCpuThreadNum);
        this.setThreadPool(Executors.newFixedThreadPool(SystemProperties.OS_CORE_NUM*SystemProperties.DEFAULT_SINGLE_CORE_THREAD_NUM));
    }

    public NioServer(ServerSocketChannel server) {
        super(server);
        this.setThreadPool(Executors.newFixedThreadPool(SystemProperties.OS_CORE_NUM*SystemProperties.DEFAULT_SINGLE_CORE_THREAD_NUM));
    }


    public void start(HandlerTaskExecutor executor) {
        try {
            logger.info("开始启动服务");
            logger.info("开始等待客户端的请求链接!");
            //接受请求-建立监听连接
            Selector selector = Selector.open();
            getServer().register(selector,SelectionKey.OP_ACCEPT);
            while(status.get()){
                if(selector.select(DefaultNetwork.DEFAULT_CONNECTION_TIMEOUT) == 0) {
                    logger.info("正在监听请求接入......");
                    continue;
                }
                Iterator<SelectionKey> selectKeyConnections = selector.selectedKeys().iterator();
                while(selectKeyConnections.hasNext()) {
                    SelectionKey request = selectKeyConnections.next();
                    /*@SuppressWarnings("unchecked")
                    Future<?> taskResult = this.getThreadPool().submit(executor);
                    if(injectResultList.get()){
                        this.resultList.add(taskResult);
                    }*/
                    selectKeyConnections.remove();
                    executor.setClientModel(request);
                    executor.setSelector(selector);
                    executor.result();
                }
            }
            logger.info("服务已关闭!");
        } catch (IOException e) {
            if(startNum.incrementAndGet() > this.getTryReconnectedTimes()){
                throw new ConnectionException("超过重试次数上限,请尝试换用其他端口启动服务!");
            }else{
                start(executor);
            }
        }
    }

    @Override
    public void start(AbstractExecutor<?, ?, ?> executor) {
        HandlerTaskExecutor realExecutor = null;
        if(executor instanceof HandlerTaskExecutor){
            realExecutor = (HandlerTaskExecutor)executor;
            start(realExecutor);
        }
    }

    /**
     * 关闭服务
     */
    @Override
    public void stop(){
        try {
            logger.info("开始关闭服务");
            //关闭服务操作
            if(status.compareAndSet(true,false)){
                this.getServer().close();
                logger.info("完成关闭服务");
            }else{
                logger.info("服务已关闭，无需关闭!");
            }
        } catch (IOException e) {
            throw new ConnectionException("无法关闭服务,系统异常!",e);
        } finally{
            if( this.getServer().isOpen()){
                try {
                    this.getServer().close();
                } catch (IOException e) {
                    throw new ConnectionException("无法关闭資源,系统异常!",e);
                }
            }
            this.getThreadPool().shutdown();
        }
    }

    @Override
    public boolean isClosed() {
        return !this.getServer().isOpen();
    }
}
